import {ElMessage, ElMessageBox} from "element-plus";
import router from "../router";
import {computed} from "vue";

/* ==================== 计算属性 ==================== */

// 获取本地静态资源
export const LOCAL_RES = computed(() => url => new URL(url, import.meta.url).href);

/* ==================== 通用工具 ==================== */

/*
 * 系统睡眠
 *
 * @param time 睡眠时间，单位毫秒
 * */
export function sleep(time) {
    return new Promise((resolve) => setTimeout(resolve, time));
}

/*
 * 判断空值
 *
 * @param value 被判断的值
 * @return true 表示不为 null 或 undefined
 * */
export function isNotNull(value) {
    return value !== null && value !== undefined && value !== '';
}

/*
 * 判断空字符串
 *
 * @param value 被判断的值
 * @return true 表示不为 null 或 undefined 或空字符串
 * */
export function isNotEmpty(value) {
    return value !== null && value !== undefined && value !== '';
}

/*
 * 日期字符串处理：1999-01-02T12:12:12 -> 1999/01/02 12:12
 *
 * @param dateStr 日期字符串
 * @return 格式化后的日期字符串
 * */
export function dateFormat(dateStr) {
    if (!dateStr)
        return null
    // 用于将六大元素处理为两位数格式（年除外）
    function toDouble(e) {
        return e < 10 ? '0' + e : e;
    }

    // 将日期字符串转为日期格式
    let date = new Date(dateStr);

    // 获取日期中的元素: 年，月，日，时，分
    let yy = toDouble(date.getFullYear());
    let mm = toDouble(date.getMonth() + 1);
    let dd = toDouble(date.getDate());
    let hh = toDouble(date.getHours());
    let mi = toDouble(date.getMinutes());

    // 返回美化后的日期字符串
    return `${yy}/${mm}/${dd} ${hh}:${mi}`;
}

/*
 * 性别代码处理：0->'女'，1->'男'，2->'保密'
 *
 * @param genderCode 性别代码
 * @return 对应的性别字符串，0女孩，1男孩，2保密
 * */
export function genderFormat(genderCode) {
    if (genderCode === '0' || genderCode === 0) return '女孩';
    if (genderCode === '1' || genderCode === 1) return '男孩';
    if (genderCode === '2' || genderCode === 2) return '保密';
    return '性别代码异常';
}

export function studentStatusFormat(statusCode) {
    if (statusCode === '1' || statusCode === 1) return '在培';
    if (statusCode === '2' || statusCode === 2) return '毕业';
    if (statusCode === '3' || statusCode === 3) return '就业';
    if (statusCode === '4' || statusCode === 4) return '休学';
    if (statusCode === '5' || statusCode === 5) return '其他';
    return '学生状态错误';
}

export function clazzStatusFormat(statusCode) {
    if (statusCode === '0' || statusCode === 0) return '待开班';
    if (statusCode === '1' || statusCode === 1) return '正在进行';
    if (statusCode === '2' || statusCode === 2) return '已结课';
    if (statusCode === '3' || statusCode === 3) return '其他';
    return '班级状态错误';
}

/* ==================== 请求工具 ==================== */

// 请求成功响应状态码
const SUCCESS = 200;
// Token即将过期响应状态码
const TOKEN_EXPIRING_SOON = 1001;

/*
 * 获取服务器响应中的数据
 *
 * @param res axios响应对象
 * @return 响应成功时返回 data 数据或 true，响应失败时返回 false
 * */
export function getResponseData(res) {

    // 若存在2层data，则直接拆除第1层data
    res = res.data.data !== undefined ? res.data : res;

    // 请求成功：返回数据或true
    if (res.code === SUCCESS) {
        return res.data !== null ? res.data : true;
    }

    // Token过期：提示重新登录，并在1秒后跳回登录页面
    if (res.code === TOKEN_EXPIRING_SOON) {
        ElMessage.warning('Token过期，请重新登录！');
        setTimeout(() => router.push('/'), 1000);
        return false;
    }

    // 请求失败：提示失败原因，返回false
    ElMessage.warning('请求失败: ' + res.data);
    return false;
}

/*
 * 异步分页查询：封装分页查询数据的异步过程
 *
 * <ul>
 *  <li> api: API请求函数名（不带小括号），必传 </li>
 *  <li> params: API请求参数，可选 </li>
 *  <li> records: 分页结果中的数据记录，必传 </li>
 *  <li> pageInfo: 分页结果中的元信息，包括当前第几页，每页多少条以及一共多少条，必传 </li>
 * </ul>
 */
export async function myPage(config) {
    const api = config['api'];
    const params = config['params'];
    const records = config['records'];
    const pageInfo = config['pageInfo'];

    // 空值保护
    if (!api || !records || !pageInfo) return;
    let res = await api(params);
    let data = getResponseData(res);
    if (data !== null) {
        records.value = data['list'] || data['records'];
        pageInfo['currentPage'] = data['pageNum'] || data['pageNumber'];
        pageInfo['pageSize'] = data['pageSize'];
        pageInfo['total'] = data['total'] || data['totalRow'];
    }
}

/*
 * 表单异步单增：封装使用表单添加数据的异步过程
 *
 * <ul>
 *  <li> form: 表单对象，必须是reactive变量，必传 </li>
 *  <li> api: API请求函数名（不带小括号），必传 </li>
 *  <li> params: API请求参数，可选 </li>
 *  <li> fn: 回调函数名（不带小括号），即当请求成功后，调用的函数，可选 </li>
 *  <li> needTip: 是否需要成功提示，默认是，可选 </li>
 *  <li> successTip: 操作成功后的提示文字，可选 </li>
 * </ul>
 * */
export async function myInsert(config) {
    const form = config['form'];
    const api = config['api'];
    const params = config['params'];
    const fn = config['fn'];
    const needTip = config['needTip'] || true;
    const successTip = config['successTip'] || `数据添加成功！`;

    // 空值保护
    if (!form || !api) return;

    // 验证表单
    form.value.validate(async (valid) => {
        // 只有全部校验规则都通过，valid参数才为true
        if (valid) {
            // 发送请求
            let data = getResponseData(await api(params));
            if (data !== null) {
                // 成功提示
                if (needTip) ElMessage.success(successTip);
                // 存在回调函数时，异步调用回调函数
                if (fn) await fn();
            }
        }
    });
}

/*
 * 表单异步单改：封装使用表单修改数据的异步过程
 *
 * <ul>
 *  <li> form: 表单对象，必须是reactive变量，必传 </li>
 *  <li> api: API请求函数名（不带小括号），必传 </li>
 *  <li> params: API请求参数，可选 </li>
 *  <li> fn: 回调函数名（不带小括号），即当请求成功后，调用的函数，可选 </li>
 *  <li> needTip: 是否需要成功提示，默认是，可选 </li>
 *  <li> successTip: 操作成功后的提示文字，可选 </li>
 *  <li> needResetForm: 是否需要重置表单，默认是，可选 </li>
 * </ul>
 */
export async function myUpdate(config) {
    config['successTip'] = config['successTip'] || `数据修改成功！`;
    await myInsert(config);
}

/*
 * 表单异步单删：按主键删除单条数据的异步过程
 *
 * <ul>
 *  <li> id: 主键，必传 </li>
 *  <li> api: API请求函数名（不带小括号），必传 </li>
 *  <li> fn: 回调函数名（不带小括号），即当请求成功后，调用的函数，可选 </li>
 *  <li> needTip: 是否需要成功提示，默认是，可选 </li>
 *  <li> confirmTip: 危险操作保护提示文字，可选 </li>
 *  <li> successTip: 操作成功后的提示文字，可选 </li>
 * </ul>
 */
export async function myRemove(config) {

    const id = config['id'];
    const api = config['api'];
    const fn = config['fn'];
    const needTip = config['needTip'] || true;
    const confirmTip = config['confirmTip'] || `即将删除1条数据，确认吗？`;
    const successTip = config['successTip'] || `数据删除成功！`;

    // 空值保护
    if (id === null || !api) return;

    // 危险操作保护
    if (!await elConfirm(confirmTip)) return;

    // 发送请求
    let res = await api(id);

    // 解析响应中的数据
    let data = getResponseData(res);
    if (data !== null) {

        // 成功提示
        if (needTip) ElMessage.success(successTip);

        // 异步调用回调函数
        if (fn) await fn();
    }
}

/*
 * 表单异步批删：封装按主键数组批量删除多条数据的异步过程
 *
 * <ul>
 *  <li> ids: 主键数组，必传 </li>
 *  <li> api: API请求函数名（不带小括号），必传 </li>
 *  <li> fn: 回调函数名（不带小括号），即当请求成功后，调用的函数，可选 </li>
 *  <li> needTip: 是否需要成功提示，默认是，可选 </li>
 *  <li> confirmTip: 危险操作保护提示文字，可选 </li>
 *  <li> successTip: 操作成功后的提示文字，可选 </li>
 * </ul>
 */
export async function myRemoveBatch(config) {

    const ids = config['ids'];
    const api = config['api'];
    const fn = config['fn'];
    const needTip = config['needTip'] || true;
    const confirmTip = config['confirmTip'] || `即将删除 ${ids.length} 条数据，确认吗？`;
    const successTip = config['successTip'] || `成功删除 ${ids.length} 条数据！`;

    // 主键数组空值保护
    if (!ids || ids.length <= 0) {
        ElMessage.warning('至少选择1项！');
        return;
    }

    // 危险操作保护
    if (!await elConfirm(confirmTip)) return;

    // 发送请求
    let res = await api(ids.join(','));

    // 解析响应中的数据
    let data = getResponseData(res);
    if (data !== null) {

        // 成功提示
        if (needTip) ElMessage.success(successTip);

        // 异步调用回调函数
        if (fn) await fn();
    }
}

/* ==================== EL工具 ==================== */

/*
 * 封装EL确认弹窗
 *
 * @param message 弹窗内容
 * @param type 弹窗类型，默认 'warning'
 * @param title 弹窗标题，默认 '注意'
 * @param draggable 是否可拖动弹窗，默认是
 * @param confirmButtonLabel 确定按钮文字，默认 '确认，我很坚定'
 * @param cancelButtonLabel 取消按钮文字，默认 '取消，我后悔了'
 * @return true确认，false取消
 */
export function elConfirm(message,
                          type = 'warning',
                          title = '注意',
                          draggable = true,
                          confirmButtonLabel = '确认，我很坚定',
                          cancelButtonLabel = '取消，我后悔了') {

    return ElMessageBox.confirm(message, title, {
        confirmButtonText: confirmButtonLabel,
        cancelButtonText: cancelButtonLabel,
        type: type,
        draggable: draggable
    }).then(() => true).catch(() => false);
}

/* ==================== Echarts工具 ==================== */

/*
 * 封装配置Echarts饼图配置对象的过程
 *
 * <ul>
 *  <li> data: 系列数据，必传 </li>
 *  <li> name: 系列名称，必传 </li>
 *  <li> radius: 饼图半径，默认60%，可选 </li>
 * </ul>
 */
export function pieChartOption(config) {
    const data = config['data'];
    const name = config['name'];
    const radius = config['radius'] || '60%';
    // 系列据
    const series = {
        type: 'pie', // 系列类型
        name: name, // 系列名称
        data: data, // 系列数据
        radius: radius, // 饼图半径
        label: {show: true} // 显示文字
    };
    // 鼠标经过时显示提示信息
    const tooltip = {};
    // 返回Echarts饼图配置对象
    return {series: [series], tooltip: tooltip};
}

/*
 * 封装配置Echarts柱图配置对象的过程
 *
 * <ul>
 *  <li> xData: X轴数据，必传 </li>
 *  <li> yData: Y轴数据，必传 </li>
 *  <li> name: 系列名称，必传 </li>
 *  <li> xName: X轴名称，必传 </li>
 *  <li> yName: Y轴名称，必传 </li>
 *  <li> barWidth: 柱图宽度，默认50%，可选 </li>
 *  <li> markMax: 是否标记最大值，默认是，可选 </li>
 *  <li> markMin: 是否标记最小值，默认是，可选 </li>
 *  <li> markAvg: 是否标记平均值，默认是，可选 </li>
 *  <li> labelShow: 是否在柱子上显示文字，默认是，可选 </li>
 *  <li> labelRotate: 文字旋转角度，默认60，可选 </li>
 * </ul>
 */
export function barChartOption(config) {

    const xData = config['xData'];
    const yData = config['yData'];
    const name = config['name'];
    const xName = config['xName'];
    const yName = config['yName'];
    const barWidth = config['barWidth'] || '50%';
    const markMax = config['markMax'] || true;
    const markMin = config['markMin'] || true;
    const markAvg = config['markAvg'] || true;
    const labelShow = config['labelShow'] || true;
    const labelRotate = config['labelRotate'] || 60;

    // 系列数据
    const series = {
        type: 'bar', // 系列类型
        name: name, // 系列名称
        data: yData, // 系列数据
        // 标记点
        markPoint: {
            data: [
                markMax ? {type: 'max', name: '最大值'} : {}, // 自动标记最大值
                markMin ? {type: 'min', name: '最小值'} : {}, // 自动标记最小值
            ]
        },
        // 标记线
        markLine: {
            data: [
                markAvg ? {type: 'average', name: '平均值'} : {} // 标记平均值线
            ]
        },
        // 图形文字
        label: {show: labelShow, rotate: labelRotate}
    };

    // 数据X轴
    const xAxis = {
        name: xName, // 名称，可选
        data: xData, // 数据数组
        nameLocation: 'end', // 名称位置，可选
        axisLine: {show: true, lineStyle: {color: '#48b', width: 2}}, // 轴线
        axisLabel: {show: true} // 文本
    };

    // 数据Y轴
    const yAxis = {
        name: yName, // 名称，可选
        nameLocation: 'end', // 名称位置，可选
        min: 0, // 最小值
        scale: true, // 自适应最大最小值
        interval: 1, // 分割刻度（步长）
        axisLine: {show: true, lineStyle: {color: '#48b', width: 2}}, // 轴线
        axisLabel: {show: true, formatter: arg => arg * 100 + '%'} // 文本
    };

    // 鼠标经过时显示提示信息
    const tooltip = {};

    // 返回配置
    return {series: [series], xAxis: xAxis, yAxis: yAxis, tooltip: tooltip, barWidth: barWidth};
}
